const fp = require('lodash/fp');

// curry
// function curry(fn) {
//     return function curried(...args) {
//         if (args.length < fn.length) {
//             return function () {
//                 return curried(...args.concat(Array.from(arguments)))
//             };
//         }
//
//         return fn(...args);
//     };
// }
//
// function sum(a, b, c) {
//     return a + b + c;
// }
//
// const c = curry(sum);
//
// console.log(c(1,2,3));
// console.log(c(1,2)(3));

// compose
// function compose(...args) {
//     return function (value) {
//         return args.reverse().reduce((acc, fn) => {
//             return fn(acc);
//         }, value);
//     }
// }
//
// console.log(compose(fp.toUpper, fp.first)('qedwfr_124'));

// memorize
// function memoize(fn) {
//     const cache = {};
//     return function (...args) {
//         const key = JSON.stringify(arguments);
//         cache[key] = cache[key] ?? fn(...args);
//         return cache[key];
//     };
// }
//
// function calcCircle(r) {
//     console.log(1);
//     return Math.PI * r ** 2;
// }
//
// const m = memoize(calcCircle);
// console.log(m(4));
// console.log(m(4));
// console.log(m(4));

// new
// function newFactory() {
//     const obj = Object.create(null);
//     Constructor = [].shift.call(arguments);
//     obj.__proto__ = Constructor.prototype;
//     const ret = Constructor.apply(obj, arguments);
//
//     return typeof ret === 'object' ? ret : obj;
// }
//
//
// const a = newFactory(Object, { prop: 10086 });
// console.log(a.prop);

// cloneDeep
// function cloneDeep(obj) {
//     let res = null;
//     if (Array.isArray(obj)) {
//         res = [];
//     } else if (typeof obj === 'object') {
//         res = {};
//     } else {
//         return obj;
//     }
//
//     for (const key in obj) {
//         if (Array.isArray(obj[key]) || typeof obj[key] === 'object') {
//             res[key] = cloneDeep(obj[key]);
//         } else {
//             res[key] = obj;
//         }
//     }
//
//     return res;
// }
//
// const a = {
//      a: 1
// };
//
// const b = {
//     b: a,
// };
//
// const c = cloneDeep(b);
// c.b.a = 2;
// console.log(a.a);
// console.log(c.b.a);
//
// const d = Object.assign({}, b);
// d.b.a = 2;
// console.log(a.a);
// console.log(d.b.a);

// bind
// Function.prototype.myBind = function(context) {
//     const self = this;
//
//     const args = Array.prototype.slice.call(arguments, 1);
//
//     const FB = function () {};
//
//     const FA = function () {
//         return self.apply(this instanceof FB ? this : context, args.concat(Array.from(arguments)));;
//     };
//
//     if(this.prototype)
//         FB.prototype = this.prototype;
//     FA.prototype = new FB();
//
//     return FA;
// };
//
// console.log.myBind(console, 1, 2, 3)();

// call
// Function.prototype.myCall = function (context) {
//     const ctx = context || window;
//     ctx.fn = this;
//
//     const arr = [];
//     for (let i = 1; i < arguments.length; i++) {
//         arr.push('arguments['+i+']');
//     }
//
//     const res = eval('ctx.fn('+arr+')');
//     delete ctx.fn;
//
//     return res;
// };
//
// console.log.myCall(console, 1, 2, 3);

// apply
// Function.prototype.myApply = function (context, arr) {
//     const ctx = context || window;
//     ctx.fn = this;
//
//     const array = [];
//     for (let i = 0; i < arr.length; i++) {
//         array.push('arr['+i+']');
//     }
//
//     const res = eval('ctx.fn('+array+')')
//     delete ctx.fn;
//
//     return res;
// }
//
// console.log.myApply(console, [1,2,3]);

// async/await
// function myAsync() {
//     return toGenerator(function*() {
//       return 1;
//     });
// };
//
// function toGenerator(genF) {
//     return new Promise(function(resolve, reject) {
//         var gen = genF();
//
//         function stepF(nextF) {
//             try {
//                 var next = nextF();
//             } catch (e) {
//                 return reject(e);
//             }
//
//             if (next.done) {
//                 return resolve(next.value);
//             }
//             Promise.resolve(next.value).then(v => {
//                 stepF(() => gen.next(v));
//             }, e => {
//                 stepF(() => gen.throw(e));
//             });
//         }
//
//         stepF(() => gen.next(undefined));
//     });
// }
//
// myAsync().then(v => console.log(v));